package com.qf.vote2203.redisvote.controller;

import cn.hutool.jwt.JWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.vote2203.dbvote.entity.Vote;
import com.qf.vote2203.utils.Constants;
import com.qf.vote2203.utils.ResultVo;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/redisvote")
public class RedisVoteController {

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    RedisTemplate redisTemplate;
    /**
     * token颁发接口。调用投票接口之前需要访问该接口获取token。
     * - 用户提交提交手机号码，每个手机号码在每分钟内可以获取一个token。
     * - 如果在1分钟内同一个手机号码多次调用该接口，返回相同的token。
     * - 如果超出一分钟，则颁发新的token。
     */
    @GetMapping("/gettoken")
    public ResultVo getToken(String phone) throws Exception {

        // 1. 检查redis中是否有该手机号对应的token，有直接返回
        String key = Constants.REDIS_JWT_PRE+phone;
        final String redisJwt = (String) redisTemplate.opsForValue().get(key);

        if (redisJwt!=null){
            // 缓存命中了，直接返回
            return ResultVo.ok(redisJwt);
        }

        // 2. 缓存未命中
        final String newToken = JWT.create().setSubject(phone)
                .setExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60))
                .setKey(Constants.SECRET.getBytes("utf-8"))
                .sign();

        // 3. 放到redis中
        redisTemplate.opsForValue().set(key,newToken,60, TimeUnit.SECONDS);

        return ResultVo.ok(newToken);
    }



    /**
     * (2) 投票接口。该接口用于给指定选手投票。
     * - 需要申请到token才能投票。没有token不能投票。
     * - 每个手机号，每分钟内只能投一票。
     * - 投票接口接收参数：用户手机号码、选手ID（候选人名字）、token （如有其他设计可自行扩展）
     * @param phone
     * @param cadidateId
     * @param token
     * @return
     */
    @PostMapping("/dovote")
    public ResultVo dovote(String phone,Integer cadidateId,@RequestHeader("token") String token) throws Exception {

        //1. 判断该用户是否能够投票
        //1.1 校验 jwt 是否有效
        final JWT jwt = JWT.of(token);
        final boolean validate = jwt.setKey(Constants.SECRET.getBytes("utf-8"))
                .validate(0);

        if (!validate){
            throw new Exception("jwt token 无效");
        }

        final Object subject = jwt.getPayload("sub");
        System.out.println(subject);

        //1.2 校验 投票间隔够不够
        String voteIntervalkey = Constants.VOTE_INTERVAL_PRE+phone;
        final Boolean exist = redisTemplate.hasKey(voteIntervalkey);

        if (exist){
            throw new Exception("投票间隔不足60秒，不能投");
        }

        //2. 进行投票业务处理
        //2.1 记录投票流水信息
        // 创建一个list结构，写入 json {phone:13511112222,candidate_id:1,time:123123123}
        final Vote vote = new Vote();
        vote.setPhone(phone);
        vote.setCandidateId(cadidateId);
        vote.setUpdateTime(new Date());


        // 开启 redis事务
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.multi();

        try {
            String voteListKey =  Constants.VOTE_DETAIL_LIST;
            redisTemplate.opsForList().rightPush(voteListKey,vote);
            //2.2 把本次投票信息累加到 候选人总票数的 zset中
            String voteResultKey = Constants.VOTE_RESULT_ZSET;
            redisTemplate.opsForZSet().incrementScore(voteResultKey,cadidateId,1);

            //2.3 禁止同一个手机号在接下来的60s内连续投票
            redisTemplate.opsForValue().set(voteIntervalkey,new Date(),60,TimeUnit.SECONDS);

            redisTemplate.exec(); // 提交事务
        } catch (Exception e) {
            e.printStackTrace();
            redisTemplate.discard();
        }

        // 将投票流水写入入mq 的指定交换机 vote_exchage
        // vote --> json
//        final ObjectMapper objectMapper = new ObjectMapper();
//        final String votejson = objectMapper.writeValueAsString(vote);
//        String routingkey = "vote."+phone;
//        rabbitTemplate.convertAndSend("vote_exchage",routingkey,votejson);

        return ResultVo.ok();
    }


    /**
     * (3) 投票数量统计接口。该接口返回统计的投票信息。
     * - 返回json即可。[{"name":"张靓颖","票数":9999},{"name":"周笔畅","票数":8888}]
     * @return
     */
    @GetMapping("/statistic")
    public ResultVo statisticVote(){

        final Set<ZSetOperations.TypedTuple> set = redisTemplate.opsForZSet().rangeByScoreWithScores(Constants.VOTE_RESULT_ZSET,
                0, Double.MAX_VALUE);

        final ArrayList<Map<String, Object>> data = new ArrayList<>();
        for (ZSetOperations.TypedTuple typedTuple : set) {
            final Object value = typedTuple.getValue();
            final Double score = typedTuple.getScore();

            final HashMap<String, Object> map = new HashMap<>();
            map.put("cadidate_id",value);
            map.put("total",score);
            data.add(map);
        }

        return ResultVo.ok(data);
    }
}
